package stackoverflow.domain.service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import stackoverflow.domain.question.Answer;
import stackoverflow.domain.question.Post;
import stackoverflow.domain.question.PostUtility;
import stackoverflow.domain.question.Question;
import stackoverflow.domain.question.QuestionRepository;
import stackoverflow.domain.question.Tag;
import stackoverflow.domain.service.impl.QuestionServiceImpl;
import stackoverflow.domain.user.User;
import stackoverflow.domain.user.UserUtility;

public class QuestionServiceTest {
	
	private static final String userName ="userForQuestionServiceTest";
	private static final String password ="passwordForQuestionServiceTest";	
	private QuestionRepository mockQuestionRepository;
	private QuestionService questionService;

	@Before
	public void setUp() {
		mockQuestionRepository = Mockito.mock(QuestionRepository.class);
		questionService = new QuestionServiceImpl(mockQuestionRepository);
	}

	@Test
	public void retrievingQuestionsThatSatisfyASingleTag() {
		List<Question> inputQuestions = new ArrayList<Question>();
		inputQuestions.add(PostUtility.createQuestionWithTags(PostUtility.createTags("Java")));
		inputQuestions.add(PostUtility.createQuestionWithTags(PostUtility.createTags("Java","TDD")));
		inputQuestions.add(PostUtility.createQuestionWithTags(PostUtility.createTags("TDD",	"JSP")));

		Mockito.when(mockQuestionRepository.findAll()).thenReturn(
				inputQuestions);

		List<Question> outputQuestions = questionService
				.viewQuestionsThatHaveTag(PostUtility.createTags("Java"));

		for (Question question : outputQuestions) {
			if (!question.getTags().contains(new Tag("Java"))) {
				Assert
						.fail("Retrieved question does not contain the expected tag");
			}
		}
	}

	@Test
	public void retrievingQuestionsThatSatisfyMultipleTags() {
		List<Question> inputQuestions = new ArrayList<Question>();

		inputQuestions.add(PostUtility.createQuestionWithTags(PostUtility.createTags("Java")));
		inputQuestions.add(PostUtility.createQuestionWithTags(PostUtility.createTags("Java","TDD")));
		inputQuestions.add(PostUtility.createQuestionWithTags(PostUtility.createTags("TDD","JSP")));

		Mockito.when(mockQuestionRepository.findAll()).thenReturn(
				inputQuestions);

		List<Question> outputQuestions = questionService.viewQuestionsThatHaveTag
			(PostUtility.createTags("Java","JSP"));

		for (Question question : outputQuestions) {
			if (!(question.getTags().contains(new Tag("Java")) || question
					.getTags().contains(new Tag("JSP")))) {
				Assert
						.fail("Retrieved question does not contain the expected tags");
			}
		}
	}

	@Test
	public void postAnswerToAQuestion() {
		Question question = PostUtility.createQuestionWithTags(PostUtility.createTags("Java"));

		Mockito.when(
				mockQuestionRepository.findQuestionById(Mockito.anyString()))
				.thenReturn(question);

		User userWhoPostsAnswer = UserUtility.createNewUser(userName, password);
		String answerText = "Try AOP, that might help";
		Answer returnedAnswer = questionService.postAnswer(userWhoPostsAnswer,
				question.getId(), answerText);

		boolean answerPostedSuccessfully = false;
		if (StringUtils.equals(answerText, returnedAnswer.getText())
				&& (userWhoPostsAnswer.equals(returnedAnswer.getPostedBy()))) {
			answerPostedSuccessfully = true;
		}
		if (!answerPostedSuccessfully)
			Assert.fail("Answer could not be posted");
		Mockito.verify(mockQuestionRepository, Mockito.times(1))
				.findQuestionById(Mockito.anyString());
		Mockito.verifyNoMoreInteractions(mockQuestionRepository);
	}

	@Test
	public void testViewQuestions() {
		List<Question> storedQuestions = new ArrayList<Question>();
		storedQuestions.add(PostUtility.createQuestionWithTags(PostUtility.createTags("Java")));
		storedQuestions.add(PostUtility.createQuestionWithTags(PostUtility.createTags("JSP")));
		
		Mockito.when(mockQuestionRepository.findAll()).thenReturn(
				storedQuestions);

		List<Question> retrievedQuestions = questionService.viewQuestions();
		for (Question question : storedQuestions) {
			Assert.assertTrue(retrievedQuestions.contains(question));
		}
		Mockito.verify(mockQuestionRepository, Mockito.times(1)).findAll();
	}

	@Test
	public void testAddQuestion() {
		User userWhoPostsQuestion = UserUtility.createNewUser(userName, password);
		String questionText = "What's CQRS?";

		Question questionToBePosted = new Question.QuestionBuilder(userWhoPostsQuestion)
			.addTags(PostUtility.createTags("Java","JSP")).postText(questionText).build();

		QuestionService questionService = new QuestionServiceImpl(mockQuestionRepository);
		Question addedQuestion = questionService.askQuestion(
				userWhoPostsQuestion, questionText, PostUtility.createTags("Java","JSP"));

		Assert.assertEquals(
				"Returned question should be same as question to be posted",
				questionToBePosted, addedQuestion);
	}
}
